package org.lex.checksum.ui;

import java.awt.Component;
import java.awt.Container;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.lex.checksum.core.AlgorithmRunner;
import org.lex.checksum.core.AlgorithmService;
import org.lex.checksum.utils.DaemonThreadFactory;
import org.lex.checksum.utils.FileNameComparator;
import org.lex.checksum.utils.FinishListener;

public class DropHandler extends DropTargetAdapter implements FinishListener {
	private Checksum mainframe = null;
	private Queue<File> droppedFileQueue = null;
	private AlgorithmRunner calculator = null;
	private ProgressUpdater monitor = null;
	private ExecutorService executor = null;

	public DropHandler(Checksum mainframe) {
		this.mainframe = mainframe;
		this.droppedFileQueue = new LinkedList<File>();
		this.calculator = new AlgorithmRunner();
		this.executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
		this.installDropHandler(mainframe);
	}

	private void installDropHandler(Container root) {
		root.setDropTarget(new DropTarget(root, this));
		// =================================================
		Stack<Container> stack = new Stack<Container>();
		stack.push(root);
		while (false == stack.empty()) {
			Component[] children = ((Container) stack.pop()).getComponents();
			if (null == children) {
				continue;
			}
			for (int i = 0, n = children.length; i < n; i++) {
				Component c = children[i];
				c.setDropTarget(new DropTarget(c, this));
				if (c instanceof Container) {
					stack.push((Container) c);
				}
			}
		}
	}

	public void drop(DropTargetDropEvent event) {
		event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK);
		Transferable trans = event.getTransferable();
		DataFlavor[] flavors = trans.getTransferDataFlavors();
		if (null == flavors || 0 == flavors.length) {
			event.rejectDrop();
			return;
		}
		this.droppedFileQueue.clear();
		for (int i = 0, n = flavors.length; i < n; i++) {
			DataFlavor data = flavors[i];
			if (data.isFlavorJavaFileListType()) {
				try {
					for (Object o : (List<?>) trans.getTransferData(data)) {
						if (o instanceof File) {
							droppedFileQueue.add((File) o);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		this.handleDroppedFiles();
	}

	public void finished() {
		File old = calculator.getFile();
		mainframe.addResult(calculator.getResult(), old);
		mainframe.setProgress(0);
	}

	private void handleDroppedFiles() {
		mainframe.setProgress(0);
		if (droppedFileQueue.isEmpty()) {
			return;
		}
		calculator = new AlgorithmRunner();
		calculator.setAlgorithm(AlgorithmService.getInstance().getAlgorithm(mainframe.getAlgorithm()));
		calculator.setFinishListener(this);
		monitor = new ProgressUpdater(mainframe);

		Future<?> lastJob = null;
		// ================================
		while (true) {
			if (droppedFileQueue.isEmpty()) {
				// queue is empty
				monitor.stop();
				break;
			}
			File file = this.droppedFileQueue.poll();
			if (null == file || false == file.exists()) {
				continue;
			}
			// file
			if (true == file.isFile()) {
				if (null != lastJob) {
					try {
						lastJob.get();
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (ExecutionException e) {
						e.printStackTrace();
					}
				}
				mainframe.setFile(file);
				calculator.setFile(file);
				monitor.monitor(calculator);
				lastJob = this.executor.submit(calculator);
				continue;
			}
			// directory
			File[] children = file.listFiles();
			if (null == children || 0 == children.length) {
				continue;
			}
			Arrays.sort(children, FileNameComparator.getInstance());
			for (int i = children.length - 1; i >= 0; i--) {
				droppedFileQueue.offer(children[i]);
			}
		}
	}
}
